﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SnapObjects.Data.Odbc
{
    internal abstract class AseRoutineParameterSchemaProvider : RoutineParameterSchemaProvider
    {
        public AseRoutineParameterSchemaProvider(DataContext dataContext) : base(dataContext)
        {
        }

        protected override IList<DynamicModel> QueryParameters(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper)
        {
            var sql = this.GetSchemaQuerySql(nameInfo, helper);

            return _dataContext.SqlExecutor.Select<DynamicModel>(sql);
        }

        protected override Task<IList<DynamicModel>> QueryParametersAsync(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper, CancellationToken cancellationToken)
        {
            var sql = this.GetSchemaQuerySql(nameInfo, helper);

            return _dataContext.SqlExecutor.SelectAsync<DynamicModel>(sql, cancellationToken);
        }

        protected abstract string GetSchemaQuerySql(RoutineNameInfo nameInfo, ISqlGenerationHelper helper);

        protected override (string, TypeMapping) GetParameterType(DynamicModel parameter, byte precision, byte scale)
        {
            (var typeName, var mapping) = base.GetParameterType(parameter, precision, scale);

            mapping = this.ChangeMappingType(mapping, precision, scale);

            return (typeName, mapping);
        }

        protected override RoutineNameInfo ParseName(string procedureName)
        {
            var nameInfo = base.ParseName(procedureName);

            if (nameInfo.Number > -1)
            {
                nameInfo.FullName = $"{nameInfo.FullName};{nameInfo.Number}";
            }

            return nameInfo;
        }

        private TypeMapping ChangeMappingType(TypeMapping mapping, byte precision, byte scale)
        {
            var type = mapping?.ClrType ?? typeof(object);
            
            if (type == typeof(decimal) &&
                !"money".Equals(mapping.DbTypeName, StringComparison.OrdinalIgnoreCase) &&
                !"smallmoney".Equals(mapping.DbTypeName, StringComparison.OrdinalIgnoreCase))
            {
                if (scale > 0)
                {
                    if (precision <= 7)
                    {
                        type = typeof(float);
                    }
                    else if (precision <= 15)
                    {
                        type = typeof(double);
                    }
                }
                else
                {
                    if (precision <= 4)
                    {
                        type = typeof(short);
                    }
                    else if (precision <= 9)
                    {
                        type = typeof(int);
                    }
                    else if (precision <= 18)
                    {
                        type = typeof(long);
                    }
                }

                var typeMapper = _dataContext.AdoDbSqlProvider.TypeMapper;

                return typeMapper.FindMapping(type);
            }

            return mapping;
        }
    }
}
